之前講了圖片訊息的處理、聲音訊息的相關應用,
今天就來介紹一個簡單的範例,
關於如果想要用位置資訊搜尋附近的地標可以怎麼做,
在LINE的官方帳號中,能夠傳送位置訊息,
這邊先簡單的用一個範例來看接收位置訊息時所獲得的資料有哪些,
首先在views.py設定顯示webhook的內容:
@csrf_exempt
def callback(request):
if request.method == 'POST':
message=[]
signature = request.META['HTTP_X_LINE_SIGNATURE']
#這邊的body即為webhook內容
body = request.body.decode('utf-8')
print(body)
然後傳送一個位置資訊進行測試:
從LINE傳送過來的webhook長這樣:
{
"events": [
{
"type": "message",
"replyToken": "1904e4f5f7ce455da107bafae7b7fc73",
"source": {
"userId": "Ub4adef4cf175e81f72f2f9ba65ea245d",
"type": "user"
},
"timestamp": 1602346694743,
"mode": "active",
"message": {
"type": "location",
"id": "12830230301657",
"title": "臺北車站",
"address": "台灣台北市中正區北平西路3號100臺灣",
"latitude": 25.047702,
"longitude": 121.517373
}
}
],
"destination": "U0a4c1d6d916fa5490ae690e6e47b7722"
}
可以看到收到的資訊當中,有幾個主要的內容:
1.event.message.address=位置的地址資訊
2.event.message.latitude=位置的緯度資訊
3.event.message.longitude=位置的經度資訊
所以我們可以知道,在位置資訊可以獲得的資料有經度跟緯度,
但是至於地址就不一定會獲得,除非用戶輸入的位置有偵測到地址,
或者是有特別輸入指定地址才會獲得,
所以如果我們希望從位置資訊當中的縣市別、鄉鎮別、街道等資訊去搜尋附近地標,
則可能會在第一時間就無法掌握地址資訊而無法繼續下一步,
因此在搜尋附近地標的時候,
可以考慮先將所有的位置都轉為經緯度座標,
再由經緯度座標去進行相對距離的條件篩選或計算,
這邊就以行政院農委會資料開放平台的糧商資訊資料庫為例,
可以看到上面的這個資料庫當中,有許多關於糧商的公開聯絡資訊,
其中就包含了營業地址,
假設我要做一個應用,是由LINE接收位置資訊,
然後由篩選出最接近我的幾個糧商資訊,該怎麼做呢?
首先我們建立一個由地址轉換成經緯度的函數,
程式名稱為address_to_coordinate.py:
#address_to_coordinate.py
import requests
import urllib.request
import json
import time
GOOGLE_API_KEY = '放入自己的google api key'
def get_latitude_longtitude(address):
# decode url
address = urllib.request.quote(address)
url = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + '&key=' + GOOGLE_API_KEY
while True:
res = requests.get(url)
js = json.loads(res.text)
if js["status"] != "OVER_QUERY_LIMIT":
time.sleep(1)
break
result = js["results"][0]["geometry"]["location"]
lat = result["lat"]
lng = result["lng"]
print(lat,lng)
return lat, lng
這個函數的作用是將收到的地址資訊,轉換成經緯度座標,
由於要使用到google的geocode API,
所以需要先在google上面申請API KEY,
依照下列步驟:
1.在Google Cloud Platform登入google帳號
2.由於google map api現在都需要綁定信用卡才能使用,每個月有免費額度,所以需要先進入帳單設定
3.設定信用卡(這邊就不示範囉,相信大家應該可以自己處理XD)
4.完成信用卡連動後,搜尋Geocoding API
5.點選啟用
6.點選左上角的選單,選擇API和服務,從憑證當中點選建立憑證,並選擇API金鑰
7.獲得API KEY
這樣就成功獲得API KEY使用權限了,
將API KEY套用在上面的程式碼中,
可以隨便用一個地址來進行測試:
回到上面的資料庫爬取部分,
我們可以設計一個由LINE接收位置資訊後,
將資料庫的地址轉為經緯度並且地區性搜尋的應用,
這邊建立一個專門做為糧商資訊搜尋並建立為Flex Message的函數,
名為Grain_Merchant.py,程式碼如下:
#Grain_Merchant.py
import requests
from bs4 import BeautifulSoup
import csv
import os
import time
#======LINE API=========
from linebot import LineBotApi, WebhookParser
from linebot.exceptions import InvalidSignatureError, LineBotApiError
from linebot.models import *
#=======================
from IT_help.address_to_coordinate import *
#糧價查詢的URL
def grain_merchant(address,latitude_o,longitude_o):
st_time = time.time()
print("地址:",address,"緯度:",latitude_o,"經度:",longitude_o)
url = 'https://data.coa.gov.tw/Service/OpenData/FromM/FoodBusinessData.aspx?$top=500&$skip=0'
region_list = ['基隆市','台北市','新北市','宜蘭縣','桃園市','新竹市','新竹縣','苗栗縣','台中市','彰化縣','南投縣','雲林縣','嘉義市','嘉義縣','台南市','高雄市','屏東縣','花蓮縣','台東縣','澎湖縣','金門縣','連江縣']
for region in region_list:
try:
if region in address:
user_region=region
except:
return TextSendMessage(text='此位置無法查詢,請再嘗試搜尋其他位置')
res_get = requests.get(url)
a = res_get.json()
contents=dict()
contents['type']='carousel'
bubbles=[]
i=1
for b in a:
ed_time = time.time()
if i<=10 and user_region in b['營業地址'] and int(ed_time-st_time)<=15:
latitude_b , longitude_b = get_latitude_longtitude(b['營業地址'])
if abs(latitude_o-latitude_b)<0.1 and abs(longitude_o-longitude_b)<0.1:
bubble = { "type": "bubble",
"body": {
"type": "box",
"layout": "vertical",
"contents": [
{
"type": "text",
"text": b['公司或商號名稱'],
"weight": "bold",
"size": "xxl",
"margin": "md",
"wrap": True,
"style": "normal"
},
{
"type": "text",
"text": "聯絡資訊",
"margin": "md",
"size": "lg",
"align": "start",
"decoration": "none"
},
{
"type": "separator"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "電話:"+b['糧商電話號碼'],
"size": "sm",
"wrap": True
}
],
"margin": "sm"
},
{
"type": "box",
"layout": "horizontal",
"contents": [
{
"type": "text",
"text": "地址:"+b['營業地址'],
"size": "sm"
}
],
"margin": "sm"
},
{
"type": "separator",
"margin": "xxl"
},
{
"type": "text",
"text": "經營業務種類",
"margin": "md",
"size": "lg",
"align": "start"
},
{
"type": "box",
"layout": "vertical",
"margin": "sm",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": b['經營業務種類'],
"size": "sm",
"wrap": True,
"align": "start"
}
]
},
{
"type": "separator",
"margin": "xxl"
},
{
"type": "text",
"text": "經營糧食種類",
"margin": "md",
"align": "start",
"size": "lg"
},
{
"type": "box",
"layout": "vertical",
"margin": "sm",
"spacing": "sm",
"contents": [
{
"type": "text",
"text": b['經營糧食種類'],
"size": "sm",
"wrap": True,
"align": "start"
}
]
},
{
"type": "separator",
"margin": "xxl"
},
{
"type": "box",
"layout": "horizontal",
"margin": "md",
"contents": [
{
"type": "text",
"text": "糧商登記證證號:",
"size": "xs",
"color": "#aaaaaa",
"flex": 0
},
{
"type": "text",
"text": b['糧商登記證證號'],
"color": "#aaaaaa",
"size": "xs",
"align": "end"
}
]
}
]
},
"styles": {
"footer": {
"separator": True
}
}
}
bubbles.append(bubble)
i+=1
ed_time=time.time()
if len(bubbles)!=0:
contents['contents']=bubbles
message = FlexSendMessage(alt_text='糧商資訊',contents=contents)
elif len(bubbles)==0:
message = TextSendMessage(text='附近未搜尋到糧商相關資訊')
return message
這邊的方法,是先將收到的位置資訊中,
以地址、緯度與經度丟入grain_merchant()函數中,
並且爬取資料庫中的前500筆資料,
執行步驟如下:
1.爬取資料庫中前500筆糧商資訊
2.將LINE位置資訊的地址中,與糧商資訊的營業地址的縣市進行比對,若縣市相同才進入下一步篩選
3.將同縣市的糧商營業地址丟到get_latitude_longtitude()當中獲得經緯度資料
4.計算LINE位置訊息與營業地址經緯度之間距離的絕對值
(緯度每度約111km、經度每度在台灣約102公里,因此0.1即10公里)
5.搜尋符合條件的10筆資料,並建立為Flex Message
然後在views.py中引入該函數:
#views.py
from IT_help.Grain_Merchant import *
...
elif event.message.type=='location':
address = event.message.address
latitude = event.message.latitude
longitude = event.message.longitude
message.append(TextSendMessage(text='位置訊息'))
message.append(grain_merchant(address,latitude,longitude))
line_bot_api.reply_message(event.reply_token,message)
完成設置,就來測試看看囉~
實在是太厲害了,只能寫個服...
不過想請問那個bubble增加的數量好像有限,記得是12個
不用程式限制住嗎?
carousel的最大上限是10塊bubble,LINE官方限制的,如果你的資料超過10個bubble的容量限制,但資料瀏覽上有超過10塊bubble的需求,可以考慮分開做成兩個carousel,或者是直接變成網頁的表格瀏覽方式喔
至於用程式限制的部分,我這邊是使用
if i<=10:
來限制迴圈的數量不要跑超過10次
不確定你的問題是不是在問這個方法
您真是內行,馬上就知道我想問什麼!
謝謝您寫這麼清楚又厲害的文件
我也有看你的youtube